/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 * $LastChangedRevision$
 */

/** @file
 *
 * @defgroup ble_sdk_lib_util Utility Functions and Definitions
 * @{
 * @ingroup ble_sdk_lib
 * @brief Various types and definitions available to all applications.
 */

#ifndef BLE_SDK_UTIL_H__
#define BLE_SDK_UTIL_H__

#include <stdint.h>
#include "compiler_abstraction.h"

#define PACKED __packed

/**@brief type for holding an encoded (i.e. little endian) 16 bit unsigned integer. */
typedef uint8_t uint16_le_t[2];

/**@brief type for holding an encoded (i.e. little endian) 32 bit unsigned integer. */
typedef uint8_t uint32_le_t[4];

/**@brief Byte array type. */
typedef struct
{
    uint16_t  size;                 /**< Number of array entries. */
    uint8_t * p_data;               /**< Pointer to array entries. */
} uint8_array_t;

/**@cond NO_DOXYGEN */
#define _GPIO_PIN_CONFIG(PIN_NO, IO_OUTPUT, IO_PULL, IO_DRIVE, IO_SENSE) \
    do{                                                                  \
        NRF_GPIO->PIN_CNF[##PIN_NO##] =                                  \
            ((IO_OUTPUT) << 0) |                                         \
            ((IO_OUTPUT) << 1) |                                         \
            ((IO_PULL)   << 2) |                                         \
            ((IO_DRIVE)  << 8) |                                         \
            ((IO_SENSE)  << 16);                                         \
    } while(0)
/**@endcond */

/**@brief Macro to configure an I/O.
 *
 *  @details This macro configures a given I/O to input or output with pullup/buffer configuration.
 *
 *  @param PIN_NO     I/O pin to configure.
 *  @param IO_OUTPUT  Indicate if the I/O is to be set as an output.
 *  @param IO_PULL    Pull resistor.
 *  @param IO_DRIVE   Buffer configuration.
 *  @param IO_SENSE   Pin sensing mechanism.
 */
#define GPIO_PIN_CONFIG(PIN_NO, IO_OUTPUT, IO_PULL, IO_DRIVE, IO_SENSE)\
            _GPIO_PIN_CONFIG(PIN_NO, IO_OUTPUT, IO_PULL, IO_DRIVE, IO_SENSE)

/**@brief Configure a PIN to be used as a LED.
 *
 *  @param PIN_NO Input pin to configure.
 */
#define GPIO_LED_CONFIG(PIN_NO)                       \
    do                                                \
    {                                                 \
        GPIO_PIN_CONFIG(PIN_NO,                       \
                        GPIO_PIN_CNF_DIR_Output,      \
                        GPIO_PIN_CNF_PULL_Disabled,   \
                        GPIO_PIN_CNF_DRIVE_S0S1,      \
                        GPIO_PIN_CNF_SENSE_Disabled); \
    } while (0)

/**@brief Configure a PIN to be used as a debug pin (e.g. to be monitored using a logic analyzer).
 *
 *  @param PIN_NO Input pin to configure.
 */
#define GPIO_DEBUG_PIN_CONFIG(PIN_NO)               \
    do                                              \
    {                                               \
        GPIO_PIN_CONFIG(PIN_NO,                     \
                      GPIO_PIN_CNF_DIR_Output,      \
                      GPIO_PIN_CNF_PULL_Disabled,   \
                      GPIO_PIN_CNF_DRIVE_S0S1,      \
                      GPIO_PIN_CNF_SENSE_Disabled); \
    } while (0)

/**@cond NO_DOXYGEN */
#define _GPIOTE_BUTTON_CONFIG(PIN_NO)           \
    (                                           \
        (GPIOTE_CONFIG_POLARITY_HiToLo << 16) | \
        ((PIN_NO) << 8) |                       \
        GPIOTE_CONFIG_MODE_Event                \
    )
/**@endcond */

/**@brief Configure a PIN to be used as a push button.
 *
 *  @param PIN_NO Input pin to configure.
 */
#define GPIOTE_BUTTON_CONFIG(PIN_NO) _GPIOTE_BUTTON_CONFIG(PIN_NO)

/**@brief Macro for entering a critical region. */
#define CRITICAL_REGION_ENTER()                                                         \
    {                                                                                   \
        uint8_t IS_NESTED_CRITICAL_REGION;                                              \
        uint32_t ERR_CODE = nrf_nvic_critical_region_enter(&IS_NESTED_CRITICAL_REGION); \
        if (ERR_CODE != NRF_SUCCESS)                                                    \
        {                                                                               \
            ASSERT(false);                                                              \
        }                                                     
    
/**@brief Macro for leaving a critical region. */
#define CRITICAL_REGION_EXIT()                                                          \
        ERR_CODE = nrf_nvic_critical_region_exit(IS_NESTED_CRITICAL_REGION);            \
        if (ERR_CODE != NRF_SUCCESS)                                                    \
        {                                                                               \
            ASSERT(false);                                                              \
        }                                                                               \
    }
    
/**@brief Perform rounded integer division (as opposed to truncating the result).
 *
 * @param[in]   A   Numerator.
 * @param[in]   B   Denominator.
 * @return      Rounded (integer) result of dividing A by B.
 */
#define ROUNDED_DIV(A, B) (((A) + ((B) / 2)) / (B))

/**@brief Perform integer division, making sure the result is rounded up.
 *
 * @details One typical use for this is to compute the number of objects with size B is needed to
 *          hold A number of bytes.
 *
 * @param[in]   A   Numerator.
 * @param[in]   B   Denominator.
 * @return      Integer result of dividing A by B, rounded up.
 */
#define CEIL_DIV(A, B)      \
    /*lint -save -e573 */   \
    ((((A) - 1) / (B)) + 1) \
    /*lint -restore */

/**@brief Inline function for encoding a uint16 value.
 *
 * @param[in]   value            Value to be encoded.
 * @param[out]  p_encoded_data   Buffer where the encoded data is to be written.
 * @return      Number of bytes written.
 */
static __INLINE uint8_t uint16_encode(uint16_t value, uint8_t * p_encoded_data)
{
    *((PACKED uint16_t *)p_encoded_data) = value;
    return sizeof(PACKED uint16_t);
}
    
/**@brief Inline function for encoding a uint32 value.
 *
 * @param[in]   value            Value to be encoded.
 * @param[out]  p_encoded_data   Buffer where the encoded data is to be written.
 * @return      Number of bytes written.
 */
static __INLINE uint8_t uint32_encode(uint32_t value, uint8_t * p_encoded_data)
{
    *((PACKED uint32_t *)p_encoded_data) = value;
    return sizeof(PACKED uint32_t);
}

/**@brief Inline function for decoding a uint16 value.
 *
 * @param[in]   p_encoded_data   Buffer where the encoded data is stored.
 * @return      Decoded value.
 */
static __INLINE uint16_t uint16_decode(const uint8_t * p_encoded_data)
{
    return *((PACKED uint16_t *)p_encoded_data);
}

/**@brief Inline function for decoding a uint32 value.
 *
 * @param[in]   p_encoded_data   Buffer where the encoded data is stored.
 * @return      Decoded value.
 */
static __INLINE uint32_t uint32_decode(const uint8_t * p_encoded_data)
{
    return *((PACKED uint32_t *)p_encoded_data);
}
    
#endif // BLE_SDK_UTIL_H__

/** @} */
